home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Source / MiscMergeKit / MME+Symbols.m < prev    next >
Encoding:
Text File  |  1996-02-07  |  6.1 KB  |  191 lines

  1. //
  2. //    MME+Symbols.m -- merge engine symbol table and variable handlers
  3. //        Written by Don Yacktman Copyright (c) 1995 by Don Yacktman.
  4. //                Version 1.0.  All rights reserved.
  5. //        This notice may not be removed from this source code.
  6. //
  7. //    This object is included in the MiscKit by permission from the author
  8. //    and its use is governed by the MiscKit license, found in the file
  9. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  10. //    for a list of all applicable permissions and restrictions.
  11. //    
  12.  
  13.  
  14. #import <misckit/misckit.h>
  15. #import <misckit/MiscMergeEngine.h>
  16. #import <misckit/MiscMergeTemplate.h>
  17. #import <misckit/MiscIfStack.h>
  18.  
  19.  
  20. @implementation MiscMergeEngine (Symbols)
  21.  
  22. // Handling the local symbol table
  23.  
  24. - resetSymbolTable
  25. /*" Clears the local symbol table.  Returns self.
  26. "*/
  27. {    // we don't free values--this could leak memory
  28.     if (symbolTable) {
  29.         [symbolTable freeObjects];
  30.         [symbolTable free];
  31.     }
  32.     symbolTable = [[MiscDictionary alloc] init];
  33.     return self;
  34. }
  35.  
  36. - (MiscString *)symbolForKey:(MiscString *)name
  37. /*" Attempts to find a symbol in the symbol table that corresponds to
  38. %{name}.  If found, the value for that symbol is returned, if not, then
  39. nil is returned.  The search is conducted through the local symbol
  40. table, through all parent merges' symbol tables, and then through the
  41. global symbol table.
  42. "*/
  43. { // if not available locally, we check the global table.
  44.     if ([symbolTable isKey:name]) {
  45.         // we have it, so return that.
  46.         return [symbolTable valueForKey:name];
  47.     } else {
  48.         if (parentMerge) {
  49.             // if parent doesn't have it, it will try it's
  50.             // parent, backing up until the global table is hit.
  51.             return [parentMerge symbolForKey:name];
  52.         }
  53.     }
  54.     // if the parent doesn't exist and we don't
  55.     // have it, then we check the globals.
  56.     return [[self class] globalSymbolForKey:name];
  57. }
  58.  
  59. - setSymbol:(MiscString *)name toValue:(MiscString *)value
  60. /*" Adds the symbol %{name} to the local symbol table with %{value} as
  61. its value.  Returns self.
  62. "*/
  63. {
  64.     [value squashSpaces]; // just to be safe
  65.     [symbolTable insertKey:name value:value];
  66.     return self;
  67. }
  68.  
  69. - (MiscString *)getField:(MiscString *)fieldName
  70. /*" Attempts to resolve a field name.  If found in the merge dictionary,
  71. then the value in the dictionary is returned.  If not found there, then
  72. a search through the symbol tables is conducted.  If there are no local
  73. or global symbols named %{fieldName} then %{fieldName} is returned.  If
  74. there are local or global symbols, however, then an attempt is made to
  75. resolve their values to a key in the merge dictionary.  If the local or
  76. global symbols exist, but cannot be resolved into values in the merge
  77. dictionary, then they local/global value is returned.
  78.  
  79. This complex search allows aliases to be created for various merge fields
  80. so that they may be accessed by different names.  It also allows
  81. redirection--if a field is missing in a particular merge, the global
  82. or local symbol tables can suggest another field to use in its place.
  83. Finally, it allows setting of default values in the local or global
  84. symbol table.  If the field is missing from the merge dictionary, then a
  85. default stored in the symbol tables can be used.
  86. "*/
  87. {
  88.     MiscString *fieldString = nil;
  89.     MiscString *fieldString2 = nil;
  90.  
  91.     [fieldName squashSpaces];
  92.     // if in the merge record, return it
  93.     if ([dictionary isKey:fieldName]) {
  94.         return [dictionary valueForKey:fieldName];
  95.     }
  96.     
  97.     fieldString = [self symbolForKey:fieldName];
  98.     // while loop allows for indirection
  99.     while (fieldString) {
  100.         if ([dictionary isKey:fieldString]) {
  101.             return [dictionary valueForKey:fieldString];
  102.         }
  103.         fieldString2 = fieldString;
  104.         fieldString = [self symbolForKey:fieldString];
  105.     }
  106.     if (fieldString2) return fieldString2;
  107.  
  108.     // Following if condition is a temporary fix for leaving out spurious
  109.     // "blank" fields at the end of the merge template.  It should really
  110.     // only have to check the state of "leaveDelimiters", not "fieldName".
  111.     if (leaveDelimiters && (fieldName != NULL) && (![fieldName emptyString])) {
  112.         MiscString *output = [MiscString new];
  113.         [output setFromFormat:"%c%s%c",
  114.                 [MiscMergeTemplate startFieldDelimiter],
  115.                 [fieldName stringValue],
  116.                 [MiscMergeTemplate endFieldDelimiter]];
  117.         return output; // these will LEAK, since not coming from dictionary.
  118.         // (other strings returned by this method shouldn't be freed!)
  119.         // No way around it until we get the foundation kit.  :-(
  120.     }
  121.     return fieldName; // treat as a literal if not found
  122. }
  123.  
  124. - setLeaveDelimiters:(BOOL)aFlag
  125. /*" Tells engine how to handle unresolveable merge fields.  If %{aFlag}
  126. is YES, then the merge delimiters will be put around the field name and
  127. that will be returned.  If NO, then the field name will be returned
  128. without the delimiters on.  (That is the default.)  Returns self.
  129. "*/
  130. {
  131.     leaveDelimiters = aFlag;
  132.     return self;
  133. }
  134.  
  135. - (BOOL)leaveDelimiters
  136. /*" Returns how the engine is set to handle unresolveable merge fields.
  137. "*/
  138. {
  139.     return leaveDelimiters;
  140. }
  141.  
  142. - resetVariables
  143. /*" Empties out the merge variables.  Returns self.
  144. "*/
  145. {    // we don't free values--this could leak memory
  146.     if (variables) {
  147.         [variables freeObjects];
  148.         [variables free];
  149.     }
  150.     variables = [[MiscDictionary alloc] init];
  151.     return self;
  152. }
  153.  
  154. - getVariableNamed:(MiscString *)name
  155. /*" Returns the merge variable named %{name}.  Returns nil if not found.
  156. "*/
  157. {
  158.     return [variables valueForKey:name];
  159. }
  160.  
  161. - setVariableNamed:(MiscString *)name to:aValue
  162. /*" Sets the merge variable named %{name} to %{aValue}.  Returns self.
  163. "*/
  164. {
  165.     [variables insertKey:name value:aValue];
  166.     return self;
  167. }
  168.  
  169. // This is a "special" variable available to all mergers; we make
  170. // sure that it exists.  Commands can obviously still install their
  171. // own variables as well...
  172. - ifStack
  173. /*" Returns the special “if stack” merge variable, creating it if
  174. necessary.  The “if stack” is used by the if/else/endif commands
  175. and also used to control turning the output of the merge on and off.
  176. "*/
  177. {
  178.     static id ifStackKey = nil;
  179.     id ifStack;
  180.  
  181.     if (!ifStackKey) ifStackKey = [MiscString newWithString:"_Misc_ifStack"];
  182.     ifStack = [self getVariableNamed:ifStackKey];
  183.     if (!ifStack) {
  184.         ifStack = [[MiscIfStack alloc] init];
  185.         [self setVariableNamed:ifStackKey to:ifStack];
  186.     }
  187.     return ifStack;
  188. }
  189.  
  190. @end
  191.